package com.coolw.data.migrate.process;

import com.coolw.data.migrate.common.constant.SqlTemplateConst;
import com.coolw.data.migrate.common.util.JdbcUtil;
import com.coolw.data.migrate.domain.request.DataMigrateRequest;
import com.coolw.data.migrate.domain.response.DataMigrateResponse;
import lombok.extern.slf4j.Slf4j;
import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.core.text.split.SplitUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;

import java.util.List;

/**
 * 数据迁移处理器
 *
 * @author coolw
 * @date 2024/2/1 15:19
 */
@Slf4j
@Component
public class DataMigrateSimpleProcess {

    /**
     * 目标表容量阈值
     */
    @Value("${migrate.targetTableMaxSize}")
    private int targetTableMaxSize;

    /**
     * 每批次迁移数量
     */
    @Value("${migrate.perBatchLimit}")
    private int perBatchLimit;

    /**
     * 允许数据迁移的源表名
     */
    @Value("${migrate.sourceTables}")
    private String sourceTables;

    /**
     * 数据迁移
     * <p>
     * 1.校验源表名是否在配置中,配置了才放行
     * 2.校验目标表是否已达到阈值
     * 3.计算本次数据迁移量
     * 4.按批次数据迁移
     *
     * @param request 数据迁移条件
     * @return 数据迁移结果
     */
    public DataMigrateResponse process(DataMigrateRequest request) {
        String targetTableName = request.getTargetTableName();
        String sourceTableName = request.getSourceTableName();
        int beforeDay = request.getBeforeDay();

        StopWatch stopWatch = new StopWatch();
        stopWatch.start("数据迁移");

        String message;
        if (beforeDay <= 0) {
            message = "beforeDay 必须大于0";
            return failHandler(stopWatch, message);
        }

        // 1.校验源表名是否在配置中
        List<String> tables = SplitUtil.split(sourceTables, ",");
        boolean allow = tables.contains(sourceTableName) || tables.contains("*");
        if (!allow) {
            message = StrUtil.format("源表名[{}]未配置,不允许做数据迁移", sourceTableName);
            return failHandler(stopWatch, message);
        }

        JdbcUtil.getConnection();
        try {
            // 2.校验目标表是否已达到阈值
            long currentTargetTableSize = JdbcUtil.getTableDataSizeByName(targetTableName);
            if (currentTargetTableSize >= targetTableMaxSize) {
                message = StrUtil.format("目标表[{}]-当前数量[{}],已超过阈值[{}]", targetTableName, currentTargetTableSize, targetTableMaxSize);
                return failHandler(stopWatch, message);
            }

            // 3.计算本次数据迁移量
            String getCountSql = StrUtil.format(SqlTemplateConst.BeforeDayEnum.COUNT_SQL_TEMPLATE_WITH_CONDITION.getSql(), sourceTableName, beforeDay);
            int sourceTotalSize = JdbcUtil.getTableDataSizeBySql(getCountSql);
            log.info("本次源表[{}]向目标表[{}]进行数据迁移,数量:{}", sourceTableName, targetTableName, sourceTotalSize);
            if (sourceTotalSize == 0) {
                message = "无满足源表条件的数据";
                return successHandler(sourceTotalSize, 0, stopWatch, message);
            }

            // 4.按批次数据迁移
            return dataMigrateHandler(request, sourceTotalSize, stopWatch);
        } finally {
            JdbcUtil.clear();
        }
    }

    private DataMigrateResponse dataMigrateHandler(DataMigrateRequest req, int sourceTotalSize, StopWatch stopWatch) {
        String targetTableName = req.getTargetTableName();
        String sourceTableName = req.getSourceTableName();
        int beforeDay = req.getBeforeDay();

        String insertSql = StrUtil.format(SqlTemplateConst.BeforeDayEnum.INSERT_SQL_TEMPLATE.getSql(), targetTableName, sourceTableName, beforeDay, perBatchLimit);
        String deleteSql = StrUtil.format(SqlTemplateConst.BeforeDayEnum.DELETE_SQL_TEMPLATE.getSql(), sourceTableName, beforeDay, perBatchLimit);

        // 已执行数据迁移量（无论成功与否）
        int migratedSize = 0;
        // 最后一个批次数据迁移量
        int lastMigrateSize = 0;
        // 成功数据迁移量
        int successSize = 0;
        // 批次计数
        int batchCount = 0;
        String message;
        do {
            // 最后一个批次
            if (migratedSize + perBatchLimit > sourceTotalSize) {
                lastMigrateSize = sourceTotalSize - migratedSize;
            }

            // 本次实际迁移数据量
            long currentActualMigrateSize = lastMigrateSize > 0 ? lastMigrateSize : perBatchLimit;

            // 校验本次数据迁移量是否小于等于目标表的剩余容量
            message = checkTargetTableThreshold(targetTableName, currentActualMigrateSize);
            if (StrUtil.isNotBlank(message)) {
                if (successSize > 0) {
                    return successHandler(sourceTotalSize, successSize, stopWatch, message);
                }
                return failHandler(stopWatch, message);
            }

            // 数据迁移SQL执行
            long startTime = System.currentTimeMillis();
            boolean success = JdbcUtil.sqlExecuteWithManualTra(insertSql, deleteSql);
            if (success) {
                successSize += currentActualMigrateSize;
            }
            migratedSize += currentActualMigrateSize;
            batchCount++;

            long remainSize = sourceTotalSize - migratedSize;
            long cost = System.currentTimeMillis() - startTime;
            log.info("第{}个批次结果:{},迁移数据量:{},剩余:{},耗时:{}ms", batchCount, success, currentActualMigrateSize, remainSize, cost);
        } while (sourceTotalSize > migratedSize);

        if (successSize <= 0) {
            message = "所有数据迁移批次任务执行失败";
            return failHandler(stopWatch, message);
        }
        return successHandler(sourceTotalSize, successSize, stopWatch, null);
    }

    /**
     * 校验本次数据迁移量是否小于等于目标表的剩余容量
     *
     * @param targetTableName          目标表名
     * @param currentActualMigrateSize 本次实际迁移数据量
     * @return 检查结果，为null代表通过
     */
    private String checkTargetTableThreshold(String targetTableName, long currentActualMigrateSize) {
        // 当前目标表数据大小
        long currentTargetTableSize = JdbcUtil.getTableDataSizeByName(targetTableName);
        // 检查目标表容量是否上限
        boolean capped = currentTargetTableSize + currentActualMigrateSize > targetTableMaxSize;
        if (capped) {
            return StrUtil.format("目标表[{}]-当前数量[{}],本次迁移数量[{}],已超过阈值[{}]", targetTableName, currentTargetTableSize
                    , currentActualMigrateSize, targetTableMaxSize);
        }
        return null;
    }

    private DataMigrateResponse successHandler(int sourceTotalSize, int successSize, StopWatch stopWatch, String message) {
        stopWatch.stop();
        DataMigrateResponse response = DataMigrateResponse.success(message, stopWatch.getTotalTimeMillis());
        response.setTotalSize(sourceTotalSize);
        response.setSuccessSize(successSize);
        response.setFailSize(sourceTotalSize - successSize);
        return response;
    }

    private DataMigrateResponse failHandler(StopWatch stopWatch, String message) {
        stopWatch.stop();
        return DataMigrateResponse.fail(message, stopWatch.getTotalTimeMillis());
    }
}
